package com.changgou.oauth.service.impl;

import com.changgou.oauth.service.AuthService;
import com.changgou.oauth.util.AuthToken;
import com.netflix.ribbon.proxy.annotation.Http;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class AuthServiceImpl implements AuthService {

    //注入发送请求对象
    @Autowired
    private RestTemplate restTemplate;

    //lb客户端对象,可以获取eureka注册中心注册的服务
    @Autowired
    private LoadBalancerClient loadBalancerClient;

    //注入redis的模板类,将令牌存入redis,StringRedisTemplate(子类)和RedisTemplate(父类)
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    //注入token存储到redis的过期时间
    @Value("${auth.ttl}")
    private long ttl;

    /**
     * 用户认证
     * @param username 用户名
     * @param password 密码
     * @param clientId 客户端id
     * @param clientSecret 客户端密码
     * @return
     */
    @Override
    public AuthToken login(String username, String password, String clientId, String clientSecret) {
        //1.申请令牌

        //构建请求地址 http://localhost:9200/oauth/token
        //获取eureka注册中心注册的服务.返回实例对象
        ServiceInstance serviceInstance = loadBalancerClient.choose("user-auth");
        //uri= http://localhost:9200
        URI uri = serviceInstance.getUri();
        String url=uri+"/oauth/token";

        //构建body,创建实现类,封装body参数
        MultiValueMap body=new LinkedMultiValueMap();
        body.add("grant_type","password");
        body.add("username",username);
        body.add("password",password);

        //构建headers,创建实现类,封装headers参数
        MultiValueMap<String, String> headers=new LinkedMultiValueMap<>();
        //传递参数:Authorization:Basic Y2hhbmdnb3U6Y2hhbmdnb3U=
        //参数:客户端id,客户端密码
        headers.add("Authorization",this.getHttpBasic(clientId,clientSecret));

        //封装请求参数:body/headers
        //泛型是个Map(接口),构造传递body,headers
        HttpEntity<MultiValueMap> requestEntity = new HttpEntity<>(body,headers);

        //后台异常处理:当后端出现了400或401,后端不对异常进行处理,直接返回给前端
        restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){
            @Override
            public void handleError(ClientHttpResponse response) throws IOException {
                //判断是否出现400和401
                if (response.getRawStatusCode()!=400&&response.getRawStatusCode()!=401){
                    //不出现,则对后端进行异常处理
                    super.handleError(response);
                }
                //出现,则不处理
            }
        });


        /**
         * 发送请求
         * 参数1:请求路径
         * 参数2:请求方式
         * 参数3:请求相关参数
         * 参数4:数据返回类型
         */
        ResponseEntity<Map> responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, Map.class);
        //获取返回数据
        Map map = responseEntity.getBody();

        //判断只要以下其中一个为null,则获取令牌失败
        if (map==null || map.get("access_token")==null || map.get("refresh_token")==null || map.get("jti")==null){
            //申请令牌失败
            throw new RuntimeException("申请令牌失败");
        }

        //2.封装结果数据
        AuthToken authToken = new AuthToken();
        authToken.setAccessToken((String) map.get("access_token"));
        authToken.setRefreshToken((String) map.get("refresh_token"));
        authToken.setJti((String) map.get("jti"));
        //3.将jti作为redis的key,将jwt作为redis的value进行数据存放
        /**
         * 参数1:key
         * 参数2:value
         * 参数3:设置token存储到redis的过期时间
         * 参数4:设置时间单位
         */
        stringRedisTemplate.boundValueOps(authToken.getJti()).set(authToken.getAccessToken(),ttl, TimeUnit.SECONDS);


        return authToken;
    }

    /**
     * 私有方法,封装headers的参数
     * @param clientId
     * @param clientSecret
     * @return
     */
    private String getHttpBasic(String clientId, String clientSecret) {
        //clientId:clientSecret,然后base64进行编码
        String value=clientId+":"+clientSecret;
        //编码:Y2hhbmdnb3U6Y2hhbmdnb3U=
        byte[] encode = Base64Utils.encode(value.getBytes());
        //拼接:Basic Y2hhbmdnb3U6Y2hhbmdnb3U=
        return "Basic "+new String(encode);
    }
}
